/** @license
jSignature v2 SVG export plugin.

*/
/**
Copyright (c) 2012 Willow Systems Corp http://willow-systems.com
MIT License <http://www.opensource.org/licenses/mit-license.php>
*/

;(function () {
  'use strict'

  /** @preserve
Simplify.js BSD 
(c) 2012, Vladimir Agafonkin
mourner.github.com/simplify-js

*/
  ;(function (a, b) {
    function c(a, b) {
      var c = a.x - b.x,
        d = a.y - b.y
      return c * c + d * d
    }
    function d(a, b, c) {
      var d = b.x,
        e = b.y,
        f = c.x - d,
        g = c.y - e,
        h
      if (f !== 0 || g !== 0)
        ((h = ((a.x - d) * f + (a.y - e) * g) / (f * f + g * g)),
          h > 1 ? ((d = c.x), (e = c.y)) : h > 0 && ((d += f * h), (e += g * h)))
      return ((f = a.x - d), (g = a.y - e), f * f + g * g)
    }
    function e(a, b) {
      var d,
        e = a.length,
        f,
        g = a[0],
        h = [g]
      for (d = 1; d < e; d++) ((f = a[d]), c(f, g) > b && (h.push(f), (g = f)))
      return (g !== f && h.push(f), h)
    }
    function f(a, c) {
      var e = a.length,
        f = typeof Uint8Array != b + '' ? Uint8Array : Array,
        g = new f(e),
        h = 0,
        i = e - 1,
        j,
        k,
        l,
        m,
        n = [],
        o = [],
        p = []
      g[h] = g[i] = 1
      while (i) {
        k = 0
        for (j = h + 1; j < i; j++) ((l = d(a[j], a[h], a[i])), l > k && ((m = j), (k = l)))
        ;(k > c && ((g[m] = 1), n.push(h), o.push(m), n.push(m), o.push(i)), (h = n.pop()), (i = o.pop()))
      }
      for (j = 0; j < e; j++) g[j] && p.push(a[j])
      return p
    }
    ;('use strict')
    var g = a
    g.simplify = function (a, c, d) {
      var g = c !== b ? c * c : 1
      return (d || (a = e(a, g)), (a = f(a, g)), a)
    }
  })(window)

  /**
	Vector class. Allows us to simplify representation and manipulation of coordinate-pair
	representing shift against (0, 0)

	@public
	@class
	@param
	@returns {Type}
	*/
  function Vector(x, y) {
    this.x = x
    this.y = y
    this.reverse = function () {
      return new this.constructor(this.x * -1, this.y * -1)
    }
    this._length = null
    this.getLength = function () {
      if (!this._length) {
        this._length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2))
      }
      return this._length
    }

    var polarity = function (e) {
      return Math.round(e / Math.abs(e))
    }
    this.resizeTo = function (length) {
      // proportionally changes x,y such that the hypotenuse (vector length) is = new length
      if (this.x === 0 && this.y === 0) {
        this._length = 0
      } else if (this.x === 0) {
        this._length = length
        this.y = length * polarity(this.y)
      } else if (this.y === 0) {
        this._length = length
        this.x = length * polarity(this.x)
      } else {
        var proportion = Math.abs(this.y / this.x),
          x = Math.sqrt(Math.pow(length, 2) / (1 + Math.pow(proportion, 2))),
          y = proportion * x
        this._length = length
        this.x = x * polarity(this.x)
        this.y = y * polarity(this.y)
      }
      return this
    }

    /**
     * Calculates the angle between 'this' vector and another.
     * @public
     * @function
     * @returns {Number} The angle between the two vectors as measured in PI.
     */
    this.angleTo = function (vectorB) {
      var divisor = this.getLength() * vectorB.getLength()
      if (divisor === 0) {
        return 0
      } else {
        // JavaScript floating point math is screwed up.
        // because of it, the core of the formula can, on occasion, have values
        // over 1.0 and below -1.0.
        return Math.acos(Math.min(Math.max((this.x * vectorB.x + this.y * vectorB.y) / divisor, -1.0), 1.0)) / Math.PI
      }
    }
  }

  function Point(x, y) {
    this.x = x
    this.y = y

    this.getVectorToCoordinates = function (x, y) {
      return new Vector(x - this.x, y - this.y)
    }
    this.getVectorFromCoordinates = function (x, y) {
      return this.getVectorToCoordinates(x, y).reverse()
    }
    this.getVectorToPoint = function (point) {
      return new Vector(point.x - this.x, point.y - this.y)
    }
    this.getVectorFromPoint = function (point) {
      return this.getVectorToPoint(point).reverse()
    }
  }

  /**
	Allows one to round a number to arbitrary precision.
	Math.round() rounds to whole only.
	Number.toFixed(precision) returns a string.
	I need float to float, but with arbitrary precision, hence:

	@public
	@function
	@param number {Number}
	@param position {Number} number of digits right of decimal point to keep. If negative, rounding to the left of decimal.
	@returns {Type}
	*/
  function round(number, position) {
    var tmp = Math.pow(10, position)
    return Math.round(number * tmp) / tmp
  }

  //	/**
  //	 * This is a simple, points-to-lines (not curves) renderer.
  //	 * Keeping it around so we can activate it from time to time and see
  //	 * if smoothing logic is off much.
  //	 * @public
  //	 * @function
  //	 * @returns {String} Like so "l 1 2 3 5' with stroke as long line chain.
  //	 */
  //	function compressstroke(stroke, shiftx, shifty){
  //		// we combine strokes data into string like this:
  //		// 'M 53 7 l 1 2 3 4 -5 -6 5 -6'
  //		// see SVG documentation for Path element's 'd' argument.
  //		var lastx = stroke.x[0]
  //		, lasty = stroke.y[0]
  //		, i
  //		, l = stroke.x.length
  //		, answer = ['M', lastx - shiftx, lasty - shifty, 'l']
  //
  //		if (l === 1){
  //			// meaning this was just a DOT, not a stroke.
  //			// instead of creating a circle, we just create a short line
  //			answer.concat(1, -1)
  //		} else {
  //			for(i = 1; i < l; i++){
  //				answer = answer.concat(stroke.x[i] - lastx, stroke.y[i] - lasty)
  //				lastx = stroke.x[i]
  //				lasty = stroke.y[i]
  //			}
  //		}
  //		return answer.join(' ')
  //	}

  function segmentToCurve(stroke, positionInStroke, lineCurveThreshold) {
    'use strict'
    // long lines (ones with many pixels between them) do not look good when they are part of a large curvy stroke.
    // You know, the jaggedy crocodile spine instead of a pretty, smooth curve. Yuck!
    // We want to approximate pretty curves in-place of those ugly lines.
    // To approximate a very nice curve we need to know the direction of line before and after.
    // Hence, on long lines we actually wait for another point beyond it to come back from
    // mousemoved before we draw this curve.

    // So for "prior curve" to be calc'ed we need 4 points
    // 	A, B, C, D (we are on D now, A is 3 points in the past.)
    // and 3 lines:
    //  pre-line (from points A to B),
    //  this line (from points B to C), (we call it "this" because if it was not yet, it's the only one we can draw for sure.)
    //  post-line (from points C to D) (even through D point is 'current' we don't know how we can draw it yet)
    //
    // Well, actually, we don't need to *know* the point A, just the vector A->B

    // Again, we can only derive curve between points positionInStroke-1 and positionInStroke
    // Thus, since we can only draw a line if we know one point ahead of it, we need to shift our focus one point ahead.
    positionInStroke += 1
    // Let's hope the code that calls us knows we do that and does not call us with positionInStroke = index of last point.

    var Cpoint = new Point(stroke.x[positionInStroke - 1], stroke.y[positionInStroke - 1]),
      Dpoint = new Point(stroke.x[positionInStroke], stroke.y[positionInStroke]),
      CDvector = Cpoint.getVectorToPoint(Dpoint)
    // Again, we have a chance here to draw only PREVIOUS line segment - BC

    // So, let's start with BC curve.
    // if there is only 2 points in stroke array (C, D), we don't have "history" long enough to have point B, let alone point A.
    // so positionInStroke should start with 2, ie
    // we are here when there are at least 3 points in stroke array.
    var Bpoint = new Point(stroke.x[positionInStroke - 2], stroke.y[positionInStroke - 2]),
      BCvector = Bpoint.getVectorToPoint(Cpoint),
      ABvector,
      rounding = 2

    if (BCvector.getLength() > lineCurveThreshold) {
      // Yey! Pretty curves, here we come!
      if (positionInStroke > 2) {
        ABvector = new Point(stroke.x[positionInStroke - 3], stroke.y[positionInStroke - 3]).getVectorToPoint(Bpoint)
      } else {
        ABvector = new Vector(0, 0)
      }
      var minlenfraction = 0.05,
        maxlen = BCvector.getLength() * 0.35,
        ABCangle = BCvector.angleTo(ABvector.reverse()),
        BCDangle = CDvector.angleTo(BCvector.reverse()),
        BtoCP1vector = new Vector(ABvector.x + BCvector.x, ABvector.y + BCvector.y).resizeTo(
          Math.max(minlenfraction, ABCangle) * maxlen
        ),
        CtoCP2vector = new Vector(BCvector.x + CDvector.x, BCvector.y + CDvector.y)
          .reverse()
          .resizeTo(Math.max(minlenfraction, BCDangle) * maxlen),
        BtoCP2vector = new Vector(BCvector.x + CtoCP2vector.x, BCvector.y + CtoCP2vector.y)

      // returing curve for BC segment
      // all coords are vectors against Bpoint
      return [
        'c', // bezier curve
        round(BtoCP1vector.x, rounding),
        round(BtoCP1vector.y, rounding),
        round(BtoCP2vector.x, rounding),
        round(BtoCP2vector.y, rounding),
        round(BCvector.x, rounding),
        round(BCvector.y, rounding)
      ]
    } else {
      return [
        'l', // line
        round(BCvector.x, rounding),
        round(BCvector.y, rounding)
      ]
    }
  }

  function lastSegmentToCurve(stroke, lineCurveThreshold) {
    'use strict'
    // Here we tidy up things left unfinished

    // What's left unfinished there is the curve between the last points
    // in the stroke
    // We can also be called when there is only one point in the stroke (meaning, the
    // stroke was just a dot), in which case there is nothing for us to do.

    // So for "this curve" to be calc'ed we need 3 points
    // 	A, B, C
    // and 2 lines:
    //  pre-line (from points A to B),
    //  this line (from points B to C)
    // Well, actually, we don't need to *know* the point A, just the vector A->B
    // so, we really need points B, C and AB vector.
    var positionInStroke = stroke.x.length - 1

    // there must be at least 2 points in the stroke.for us to work. Hope calling code checks for that.
    var Cpoint = new Point(stroke.x[positionInStroke], stroke.y[positionInStroke]),
      Bpoint = new Point(stroke.x[positionInStroke - 1], stroke.y[positionInStroke - 1]),
      BCvector = Bpoint.getVectorToPoint(Cpoint),
      rounding = 2

    if (positionInStroke > 1 && BCvector.getLength() > lineCurveThreshold) {
      // we have at least 3 elems in stroke
      var ABvector = new Point(stroke.x[positionInStroke - 2], stroke.y[positionInStroke - 2]).getVectorToPoint(Bpoint),
        ABCangle = BCvector.angleTo(ABvector.reverse()),
        minlenfraction = 0.05,
        maxlen = BCvector.getLength() * 0.35,
        BtoCP1vector = new Vector(ABvector.x + BCvector.x, ABvector.y + BCvector.y).resizeTo(
          Math.max(minlenfraction, ABCangle) * maxlen
        )

      return [
        'c', // bezier curve
        round(BtoCP1vector.x, rounding),
        round(BtoCP1vector.y, rounding),
        round(BCvector.x, rounding), // CP2 is same as Cpoint
        round(BCvector.y, rounding), // CP2 is same as Cpoint
        round(BCvector.x, rounding),
        round(BCvector.y, rounding)
      ]
    } else {
      // Since there is no AB leg, there is no curve to draw. This is just line
      return [
        'l', // simple line
        round(BCvector.x, rounding),
        round(BCvector.y, rounding)
      ]
    }
  }

  function addstroke(stroke, shiftx, shifty) {
    'use strict'
    // we combine strokes data into string like this:
    // 'M 53 7 l 1 2 c 3 4 -5 -6 5 -6'
    // see SVG documentation for Path element's 'd' argument.
    var lines = [
        'M', // move to
        round(stroke.x[0] - shiftx, 2),
        round(stroke.y[0] - shifty, 2)
      ],
      // processing all points but first and last.
      i = 1, // index zero item in there is STARTING point. we already extracted it.
      l = stroke.x.length - 1, // this is a trick. We are leaving last point coordinates for separate processing.
      lineCurveThreshold = 1

    for (; i < l; i++) {
      lines.push.apply(lines, segmentToCurve(stroke, i, lineCurveThreshold))
    }
    if (l > 0 /* effectively more than 1, since we "-1" above */) {
      lines.push.apply(lines, lastSegmentToCurve(stroke, i, lineCurveThreshold))
    } else if (l === 0) {
      // meaning we only have ONE point in the stroke (and otherwise refer to the stroke as "dot")
      lines.push.apply(lines, ['l', 1, 1])
    }
    return lines.join(' ')
  }

  function simplifystroke(stroke) {
    var d = [],
      newstroke = { 'x': [], 'y': [] },
      i,
      l

    for (i = 0, l = stroke.x.length; i < l; i++) {
      d.push({ 'x': stroke.x[i], 'y': stroke.y[i] })
    }
    d = simplify(d, 0.7, true)
    for (i = 0, l = d.length; i < l; i++) {
      newstroke.x.push(d[i].x)
      newstroke.y.push(d[i].y)
    }
    return newstroke
  }

  // generate SVG style from settings
  function styleFromSettings(settings) {
    var styles = []
    var meta = [
      // ["style attr", "key in settings", "default value"]
      ['fill', undefined, 'none'],
      ['stroke', 'color', '#000000'],
      ['stroke-width', 'lineWidth', 2],
      ['stroke-linecap', undefined, 'round'],
      ['stroke-linejoin', undefined, 'round']
    ]
    for (var i = meta.length - 1; i >= 0; i--) {
      var attr = meta[i][0],
        key = meta[i][1],
        defaultVal = meta[i][2]
      styles.push(attr + '="' + (key in settings && settings[key] ? settings[key] : defaultVal) + '"')
    }
    return styles.join(' ')
  }

  function compressstrokes(data, settings) {
    'use strict'
    var answer = [
        '<?xml version="1.0" encoding="UTF-8" standalone="no"?>',
        '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'
      ],
      i,
      l = data.length,
      stroke,
      xlimits = [],
      ylimits = [],
      sizex = 0,
      sizey = 0,
      shiftx = 0,
      shifty = 0,
      minx,
      maxx,
      miny,
      maxy,
      padding = 1,
      simplifieddata = []

    if (l !== 0) {
      for (i = 0; i < l; i++) {
        stroke = simplifystroke(data[i])
        simplifieddata.push(stroke)
        xlimits = xlimits.concat(stroke.x)
        ylimits = ylimits.concat(stroke.y)
      }

      minx = Math.min.apply(null, xlimits) - padding
      maxx = Math.max.apply(null, xlimits) + padding
      miny = Math.min.apply(null, ylimits) - padding
      maxy = Math.max.apply(null, ylimits) + padding
      shiftx = minx < 0 ? 0 : minx
      shifty = miny < 0 ? 0 : miny
      sizex = maxx - minx
      sizey = maxy - miny
    }

    answer.push(
      '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="' +
        sizex.toString() +
        '" height="' +
        sizey.toString() +
        '">'
    )

    //		// This is a nice idea: use style declaration on top, and mark the lines with 'class="f"'
    //		// thus saving space in svg...
    //		// alas, many SVG renderers don't understand "class" and render the strokes in default "fill = black, no stroke" style. Ugh!!!
    //		// TODO: Rewrite ImageMagic / GraphicsMagic, InkScape, http://svg.codeplex.com/ to support style + class. until then, we hardcode the stroke style within the path.
    //		answer.push(
    //			'<style type="text/css"><![CDATA[.f {fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}]]></style>'
    //		)

    //		// This set is accompaniment to "simple line renderer" - compressstroke
    //		answer.push(
    //			'<style type="text/css"><![CDATA[.t {fill:none;stroke:#FF0000;stroke-width:2}]]></style>'
    //		)
    //		for(i = 0; i < l; i++){
    //			stroke = data[i]
    //			// This one is accompaniment to "simple line renderer"
    //			answer.push('<path class="t" d="'+ compressstroke(stroke, shiftx, shifty) +'"/>')
    //		}

    for (i = 0, l = simplifieddata.length; i < l; i++) {
      stroke = simplifieddata[i]
      answer.push('<path ' + styleFromSettings(settings) + ' d="' + addstroke(stroke, shiftx, shifty) + '"/>')
    }
    answer.push('</svg>')
    return answer.join('')
  }

  if (typeof btoa !== 'function') {
    var btoa = function (data) {
      /** @preserve
base64 encoder
MIT, GPL
http://phpjs.org/functions/base64_encode
+   original by: Tyler Akins (http://rumkin.com)
+   improved by: Bayron Guevara
+   improved by: Thunder.m
+   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+   bugfixed by: Pellentesque Malesuada
+   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+   improved by: Rafal Kukawski (http://kukawski.pl)

*/
      var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
        b64a = b64.split(''),
        o1,
        o2,
        o3,
        h1,
        h2,
        h3,
        h4,
        bits,
        i = 0,
        ac = 0,
        enc = '',
        tmp_arr = []

      do {
        // pack three octets into four hexets
        o1 = data.charCodeAt(i++)
        o2 = data.charCodeAt(i++)
        o3 = data.charCodeAt(i++)

        bits = (o1 << 16) | (o2 << 8) | o3

        h1 = (bits >> 18) & 0x3f
        h2 = (bits >> 12) & 0x3f
        h3 = (bits >> 6) & 0x3f
        h4 = bits & 0x3f

        // use hexets to index into b64, and append result to encoded string
        tmp_arr[ac++] = b64a[h1] + b64a[h2] + b64a[h3] + b64a[h4]
      } while (i < data.length)

      enc = tmp_arr.join('')
      var r = data.length % 3
      return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3)

      // end of base64 encoder MIT, GPL
    }
  }

  var unencodedmime = 'image/svg+xml'
  function getUnencodedSVG(data, settings) {
    return [unencodedmime, compressstrokes(data, settings)]
  }

  var base64encodedmime = 'image/svg+xml;base64'
  function getBase64encodedSVG(data, settings) {
    return [base64encodedmime, btoa(compressstrokes(data, settings))]
  }

  function Initializer($) {
    var mothership = $.fn['jSignature']
    mothership(
      'addPlugin',
      'export',
      'svg', // alias
      getUnencodedSVG
    )
    mothership(
      'addPlugin',
      'export',
      unencodedmime, // full name
      getUnencodedSVG
    )
    mothership(
      'addPlugin',
      'export',
      'svgbase64', // alias
      getBase64encodedSVG
    )
    mothership(
      'addPlugin',
      'export',
      base64encodedmime, // full name
      getBase64encodedSVG
    )
  }

  //  //Because plugins are minified together with jSignature, multiple defines per (minified) file blow up and dont make sense
  //	//Need to revisit this later.

  if (typeof $ === 'undefined') {
    throw new Error('We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...')
  }
  Initializer($)
})()
